{
 "nbformat": 4,
 "nbformat_minor": 0,
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "cells": [
  {
   "cell_type": "code",
   "source": [
    "# Copyright 2023 The Cirq Developers\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#     https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ],
   "metadata": {
    "id": "0w8r0YTyFMC4"
   },
   "execution_count": 1,
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "source": [
    "# Quantum utility\n",
    "\n",
    "This colab contains code for simulating a circuit described in [1] on a subset\n",
    "of qubits sufficient for reproducing the results in Fig. 4a. Running this simulation requires ~24GB of RAM, which may require a local runtime.\n",
    "Additional RAM and/or compute cores can improve performance.\n",
    "\n",
    "[1] Kim, Y., Eddins, A., Anand, S. et al. Evidence for the utility of quantum\n",
    "computing before fault tolerance. Nature 618, 500–505 (2023).\n",
    "https://doi.org/10.1038/s41586-023-06096-3"
   ],
   "metadata": {
    "id": "cA7ijEELYIh2"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "try:\n",
    "    import cirq\n",
    "except ImportError:\n",
    "    print(\"installing cirq...\")\n",
    "    !pip install --quiet cirq\n",
    "    print(\"installed cirq.\")\n",
    "\n",
    "try:\n",
    "    import qsimcirq\n",
    "except ImportError:\n",
    "    print(\"installing qsimcirq...\")\n",
    "    !pip install --quiet qsimcirq\n",
    "    print(\"installed qsimcirq.\")"
   ],
   "metadata": {
    "id": "kyyLlwIt2cs1"
   },
   "execution_count": 2,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "import cirq\n",
    "import qsimcirq\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import sympy"
   ],
   "metadata": {
    "id": "oxlQXSsI2dOC"
   },
   "execution_count": 3,
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "id": "cMvJ0vVs0MVr"
   },
   "outputs": [],
   "source": [
    "# These 30 qubits contain the measured observable:\n",
    "#\n",
    "#   37-38-39-40-41-42         .   X--Y--39-Y--X--Y          .\n",
    "#   |           |             .   |           |             .\n",
    "#   52          53            .   X          53             .\n",
    "#   |           |             .   |           |             .\n",
    "#   56-57-58-59-60-61-62-63   .   X--57-X--59-60-61-X--Y    .\n",
    "#         |           |       .         |           |       .\n",
    "#         71          72      .         71          Y       .\n",
    "#         |           |       .         |           |       .\n",
    "#   75-76-77-78-79-80-81      .   Z--76-77-78-X--Y--81      .\n",
    "#   |           |             .   |           |             .\n",
    "#   90          91            .   Y           Y             .\n",
    "#   |           |             .   |           |             .\n",
    "#   94-95       98            .   94-95       98            .\n",
    "#\n",
    "qubit_ids = [\n",
    "    *range(37, 43),  # row 0\n",
    "    *range(52, 54),  # row 1\n",
    "    *range(56, 64),  # row 2\n",
    "    *range(71, 73),  # row 3\n",
    "    *range(75, 82),  # row 4\n",
    "    *range(90, 92),  # row 5\n",
    "    *(94, 95, 98),  # row 6\n",
    "]\n",
    "q = {i: cirq.NamedQubit(f'q{i}') for i in qubit_ids}\n",
    "qubits = list(q.values())\n",
    "\n",
    "# This parameter will be used to sweep over X rotation angles.\n",
    "theta = sympy.Symbol('theta')\n",
    "x_rotations = cirq.Moment(cirq.rx(theta).on_each(qubits))\n",
    "\n",
    "# This is the ZZ(-pi/2) gate described in equation (2).\n",
    "zz_pi_2 = cirq.ZZ**-0.5\n",
    "\n",
    "# Each of these moments performs ZZ interactions along\n",
    "# 1/3 of the edges in the region, corresponding to the\n",
    "# red, blue, and green edges in Fig. 1b.\n",
    "zz_layer_1 = cirq.Moment(\n",
    "    zz_pi_2(q[37], q[52]),\n",
    "    zz_pi_2(q[41], q[53]),\n",
    "    zz_pi_2(q[56], q[57]),\n",
    "    zz_pi_2(q[58], q[71]),\n",
    "    zz_pi_2(q[59], q[60]),\n",
    "    zz_pi_2(q[61], q[62]),\n",
    "    zz_pi_2(q[72], q[81]),\n",
    "    zz_pi_2(q[76], q[77]),\n",
    "    zz_pi_2(q[78], q[79]),\n",
    "    zz_pi_2(q[94], q[95]),\n",
    ")\n",
    "zz_layer_2 = cirq.Moment(\n",
    "    zz_pi_2(q[38], q[39]),\n",
    "    zz_pi_2(q[40], q[41]),\n",
    "    zz_pi_2(q[53], q[60]),\n",
    "    zz_pi_2(q[57], q[58]),\n",
    "    zz_pi_2(q[62], q[63]),\n",
    "    zz_pi_2(q[71], q[77]),\n",
    "    zz_pi_2(q[75], q[76]),\n",
    "    zz_pi_2(q[79], q[80]),\n",
    "    zz_pi_2(q[90], q[94]),\n",
    "    zz_pi_2(q[91], q[98]),\n",
    ")\n",
    "zz_layer_3 = cirq.Moment(\n",
    "    zz_pi_2(q[37], q[38]),\n",
    "    zz_pi_2(q[39], q[40]),\n",
    "    zz_pi_2(q[41], q[42]),\n",
    "    zz_pi_2(q[52], q[56]),\n",
    "    zz_pi_2(q[58], q[59]),\n",
    "    zz_pi_2(q[60], q[61]),\n",
    "    zz_pi_2(q[62], q[72]),\n",
    "    zz_pi_2(q[75], q[90]),\n",
    "    zz_pi_2(q[77], q[78]),\n",
    "    zz_pi_2(q[79], q[91]),\n",
    "    zz_pi_2(q[80], q[81]),\n",
    ")\n",
    "\n",
    "# This circuit encapsulates a single \"step\", as shown\n",
    "# in Fig. 1a.\n",
    "step = cirq.FrozenCircuit(x_rotations, zz_layer_1, zz_layer_2, zz_layer_3)\n",
    "# Uncomment this line to print the circuit diagram for\n",
    "# a single step of the circuit.\n",
    "# print(step)"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# The circuit used to generate Fig. 4a consists of 5 steps + final rotations.\n",
    "# Changing \"repetitions\" here will adjust the number of steps simulated.\n",
    "all_steps = cirq.CircuitOperation(step, repetitions=5)\n",
    "circuit = cirq.Circuit(all_steps, x_rotations)\n",
    "\n",
    "# These are the component parts of the observable used in Fig. 4a.\n",
    "x_obs = cirq.DensePauliString('X' * 8).on(*(q[i] for i in [37, 41, 52, 56, 57, 58, 62, 79]))\n",
    "y_obs = cirq.DensePauliString('Y' * 8).on(*(q[i] for i in [38, 40, 42, 63, 72, 80, 90, 91]))\n",
    "z_obs = cirq.DensePauliString('Z').on(q[75])\n",
    "observables = [x_obs * y_obs * z_obs]\n",
    "\n",
    "# These are approximately the values of theta plotted for experimental values\n",
    "# in Fig. 4a. Changing this list will adjust the simulation to test other\n",
    "# theta values.\n",
    "theta_values = [0, 0.25, 0.5, 1, *np.linspace(1.2, 1.5, 7), np.pi / 2]\n",
    "params = cirq.Points(key=\"theta\", points=theta_values)\n",
    "\n",
    "# These options are used to tune qsim performance.\n",
    "# On CPU, \"cpu_threads\" should be set to the number of cores available.\n",
    "opt = qsimcirq.QSimOptions(max_fused_gate_size=4, cpu_threads=24)\n",
    "# To use GPU instead, uncomment this line:\n",
    "# opt = qsimcirq.QSimOptions(use_gpu=True, gpu_mode=1)\n",
    "simulator = qsimcirq.QSimSimulator(qsim_options=opt)"
   ],
   "metadata": {
    "id": "d3eqfPR_K4SY"
   },
   "execution_count": 5,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "%%time\n",
    "# This will log after each value of theta is simulated. Its purpose is to\n",
    "# give an indication of total runtime before all simulations finish.\n",
    "results = []\n",
    "for i, result in enumerate(\n",
    "    simulator.simulate_expectation_values_sweep_iter(circuit, observables, params)\n",
    "):\n",
    "    results.append(result)\n",
    "    print(f\"Completed theta={theta_values[i]:.3f}; value={result}\")\n",
    "\n",
    "# Runtimes logged in the output of this cell were achieved using a machine with\n",
    "# 24 cores and 80GB of RAM."
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "jabNt3Q72QWa",
    "outputId": "55258f37-a139-446f-cded-15c2b8ed5b35"
   },
   "execution_count": 6,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Completed theta=0.000; value=[0j]\n",
      "Completed theta=0.250; value=[(4.377516467318132e-10-3.8454568700662104e-17j)]\n",
      "Completed theta=0.500; value=[(-5.663425648032262e-07-6.273358064614394e-15j)]\n",
      "Completed theta=1.000; value=[(-0.002025490629580058-1.285792463169913e-12j)]\n",
      "Completed theta=1.200; value=[(-0.08619230321475829-4.390203478532584e-13j)]\n",
      "Completed theta=1.250; value=[(-0.16428450008848153-1.3782378016635732e-15j)]\n",
      "Completed theta=1.300; value=[(-0.28045473459690645-6.927717947913248e-13j)]\n",
      "Completed theta=1.350; value=[(-0.43263143032440227+2.2862267634593536e-13j)]\n",
      "Completed theta=1.400; value=[(-0.6074061541964849+7.668017696499385e-13j)]\n",
      "Completed theta=1.450; value=[(-0.7798960015332366-1.6847268480453537e-12j)]\n",
      "Completed theta=1.500; value=[(-0.9182744564246872+4.436968438601037e-13j)]\n",
      "Completed theta=1.571; value=[(-0.9999907156716326-4.9043406579730134e-17j)]\n",
      "CPU times: user 2h 3min 1s, sys: 4min, total: 2h 7min 2s\n",
      "Wall time: 6min 8s\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# Each element of \"results\" is a list of one expectation value.\n",
    "# For visual simplicity, the results are negated.\n",
    "plot_results = [-x[0].real for x in results]\n",
    "\n",
    "# Plot the results in the format of Fig. 4a.\n",
    "plt.plot(np.array(theta_values), plot_results, 'bo')\n",
    "plt.xlabel(r\"$ R_X $ angle $ \\theta_h $\")\n",
    "plt.ylabel(r\"$ \\langle X_{37,41,52,56,57,58,62,79} Y_{38,40,42,63,72,80,90,91} Z_{75} \\rangle $\")\n",
    "plt.xticks(np.linspace(0, np.pi / 2, 5), [\"0\", \"π/8\", \"π/4\", \"3π/8\", \"π/2\"])\n",
    "plt.yticks(np.linspace(0, 1, 6))\n",
    "plt.show()"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 283
    },
    "id": "1jiA6Yy7KGpy",
    "outputId": "99ae5c8c-8bfb-4985-861c-da1f8f4108ca"
   },
   "execution_count": 7,
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ],
      "image/png": "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\n"
     },
     "metadata": {
      "needs_background": "light"
     }
    }
   ]
  }
 ]
}
